/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.client.dialog.schema.view;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import offset.nodes.client.chooser.view.TypeChooserDialog;
import offset.nodes.client.model.NodeTypes;
import offset.nodes.client.virtual.model.SchemaNode;
import offset.nodes.client.tree.model.TreeNodePanel;
import offset.nodes.client.tree.model.TreeNodePanelContainer;
import offset.nodes.client.model.TreeNodeUserObject;
import offset.nodes.client.virtual.model.jcr.QName;
import offset.nodes.client.virtual.model.jcr.nodetype.NodeDefinition;
import offset.nodes.client.virtual.model.jcr.nodetype.NodeTypeDefinition;

/**
 *
 * @author  Walter Lütgenau
 */
public class SchemaNodePanel extends javax.swing.JPanel implements TreeNodePanel {

    TreeNodePanelContainer container = null;
    TreeNodeUserObject userObject = null;
    TextFieldDocumentListener textFieldDocumentListener = new TextFieldDocumentListener();
    TypeChooserDialog chooser = null;
    private NodeTypes types;

    class TextFieldDocumentListener implements DocumentListener {

        public void changedUpdate(DocumentEvent e) {
            validateOkButton();
        }

        public void insertUpdate(DocumentEvent e) {
            validateOkButton();
        }

        public void removeUpdate(DocumentEvent e) {
            validateOkButton();
        }
    }

    /** Creates new form SchemaPropertyPanel */
    public SchemaNodePanel() {
        initComponents();

    }

    public TreeNodeUserObject getUserObject() {
        getNodeType((SchemaNode) userObject);
        return userObject;
    }

    public void setContainer(TreeNodePanelContainer container) {
        this.container = container;
    }

    public void setPanel(TreeNodeUserObject userObject) {
        this.userObject = userObject;
        setNodeType((SchemaNode) userObject);
    }

    protected void setNodeType(SchemaNode definition) {
        if (definition.getNode() != null) {
            getMandatoryCheckbox().setSelected(definition.getNode().isMandatory());
            getMultipleCheckbox().setSelected(definition.getNode().allowsSameNameSiblings());
            getMandatoryCheckbox().setEnabled(true);
            getMultipleCheckbox().setEnabled(true);
            mandatoryLabel.setEnabled(true);
            multipleLabel.setEnabled(true);
            mixinCheckbox.setEnabled(true);
            getNameTextField().setText(definition.getName().toString());
            getNameTextField().setEnabled(true);
            primaryTypeButton.setEnabled(true);
            superTypeButton.setEnabled(false);
            superTypeTextField.setEnabled(false);
            mixinTypeButton.setEnabled(false);
            mixinList.setEnabled(false);

        } else {
            getMandatoryCheckbox().setSelected(false);
            getMultipleCheckbox().setSelected(false);
            mixinCheckbox.setSelected(false);
            getMandatoryCheckbox().setEnabled(false);
            getMultipleCheckbox().setEnabled(false);
            mandatoryLabel.setEnabled(false);
            getNameTextField().setText("");
            getNameTextField().setEnabled(false);
            primaryTypeButton.setEnabled(false);
            superTypeButton.setEnabled(true);
            superTypeTextField.setEnabled(true);
            mixinTypeButton.setEnabled(true);
            mixinList.setEnabled(true);
        }

        getPrimaryTypeTextField().setText(definition.getNodeType().getName().toString());
        mixinCheckbox.setSelected(definition.getNodeType().isMixin());
        superTypeTextField.setText(getSupertype(definition.getNodeType().getSupertypes()));
        ((DefaultListModel)mixinList.getModel()).clear();
        Iterator i = getMixinTypes(definition).iterator();
        while (i.hasNext())
            ((DefaultListModel)mixinList.getModel()).addElement(i.next());
 
        validateOkButton();
    }

    protected QName toQName(String name) {
        if ( name == null || name.length() == 0)
            return null;
        
        String[] names = name.split("}");
        return new QName(names[0].substring(1), names[1]);
    }

    protected void setSuperType(NodeTypeDefinition nodeType, QName supertype) {
        QName[] supertypes = nodeType.getSupertypes();

        for (int i = 0; i < supertypes.length; i++) {
            if (supertypes[i].equals(supertype)) { // already present
                return;
            }
        }

        QName[] result = new QName[1];
        result[0] = supertype;

        nodeType.setSupertypes(result);
    }

    protected void addSuperType(NodeTypeDefinition nodeType, QName supertype) {
        if ( supertype == null)
            return;
        
        QName[] supertypes = nodeType.getSupertypes();

        for (int i = 0; i < supertypes.length; i++) {
            if (supertypes[i].equals(supertype)) { // already present
                return;
            }
        }

        QName[] result = new QName[supertypes.length+1];
        int i;
        for( i=0; i<supertypes.length; i++) {
            result[i] = supertypes[i];
        }
        result[i] = supertype;

        nodeType.setSupertypes(result);
    }
    
    protected String getSupertype(QName[] supertypes) {
        
        for( int i=0; i<supertypes.length; i++) {
            NodeTypeDefinition nodeType = types.get(supertypes[i]);
            if ( nodeType != null && !nodeType.isMixin())
                return supertypes[i].toString();
        }
        
        return "";
    }

    protected List<String> getMixinTypes(SchemaNode definition) {
        LinkedList<String> result = new LinkedList<String>();
        
        QName[] supertypes = definition.getNodeType().getSupertypes();
        for( int i=0; i<supertypes.length; i++) {
            NodeTypeDefinition nodeType = types.get(supertypes[i]);
            if ( nodeType != null && nodeType.isMixin())
                result.add(supertypes[i].toString());
        }
        
        return result;
    }

    protected SchemaNode getNodeType(SchemaNode definition) {
        QName name = new QName("", "");

        if (definition.getNode() != null) {
            name = toQName(getNameTextField().getText());
        } else {
            name = toQName(getPrimaryTypeTextField().getText());
        }

        QName typeName = toQName(getPrimaryTypeTextField().getText());

        NodeDefinition node = (NodeDefinition) definition.getNode();
        if (node != null) {
            node.setName(name);
            node.setRequiredPrimaryTypes(new QName[]{typeName});
            node.setDefaultPrimaryType(typeName);
            node.setMandatory(getMandatoryCheckbox().isSelected());
            node.setAllowsSameNameSiblings(getMultipleCheckbox().isSelected());
        }

        NodeTypeDefinition type = definition.getNodeType();
        type.setName(typeName);
        type.setMixin(mixinCheckbox.isSelected());
        setSuperType(type, toQName(superTypeTextField.getText()));
        for (int i=0; i<mixinList.getModel().getSize(); i++)
            addSuperType(type, toQName((String) mixinList.getModel().getElementAt(i)));
        

        definition.setName(name);
        definition.setNode(node);
        definition.setType(type);

        return definition;
    }

    protected boolean isQName(String name) {
        if (name.length() == 0) {
            return false;
        }
        
        String[] parts = name.split("\\{");
        if (parts.length != 2) {
            return false;
        }

        parts = name.split("\\}");
        if (parts.length != 2) {
            return false;
        }

        return true;
    }

    private void validateOkButton() {
        if (getNameTextField().isEnabled() && !isQName(getNameTextField().getText())) {
            container.enableOkButton(false);
            return;
        }

        if (!isQName(getPrimaryTypeTextField().getText())) {
            container.enableOkButton(false);
            return;
        }
        
        if (superTypeTextField.isEnabled() && !isQName(superTypeTextField.getText())) {
            container.enableOkButton(false);
            return;
        }

        container.enableOkButton(true);
    }

    public javax.swing.JCheckBox getMandatoryCheckbox() {
        return mandatoryCheckbox;
    }

    public void setMandatoryCheckbox(javax.swing.JCheckBox mandatoryCheckbox) {
        this.mandatoryCheckbox = mandatoryCheckbox;
    }

    public javax.swing.JCheckBox getMultipleCheckbox() {
        return multipleCheckbox;
    }

    public void setMultipleCheckbox(javax.swing.JCheckBox multipleCheckbox) {
        this.multipleCheckbox = multipleCheckbox;
    }

    public javax.swing.JTextField getNameTextField() {
        return nameTextField;
    }

    public void setNameTextField(javax.swing.JTextField nameTextField) {
        this.nameTextField = nameTextField;
    }

    public javax.swing.JTextField getPrimaryTypeTextField() {
        return primaryTypeTextField;
    }

    public void setPrimaryTypeTextField(javax.swing.JTextField primaryTypeTextField) {
        this.primaryTypeTextField = primaryTypeTextField;
    }

    public void setTypes(NodeTypes types) {
        this.types = types;
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        nameLabel = new javax.swing.JLabel();
        nameTextField = new javax.swing.JTextField();
        nameTextField.getDocument().addDocumentListener(textFieldDocumentListener);
        mandatoryLabel = new javax.swing.JLabel();
        mandatoryCheckbox = new javax.swing.JCheckBox();
        multipleLabel = new javax.swing.JLabel();
        multipleCheckbox = new javax.swing.JCheckBox();
        nodeTypeLabel = new javax.swing.JLabel();
        jSeparator1 = new javax.swing.JSeparator();
        primaryTypeTextField = new javax.swing.JTextField();
        primaryTypeTextField.getDocument().addDocumentListener(textFieldDocumentListener);
        primaryTypeButton = new javax.swing.JButton();
        superTypeLabel = new javax.swing.JLabel();
        superTypeTextField = new javax.swing.JTextField();
        superTypeTextField.getDocument().addDocumentListener(textFieldDocumentListener);
        superTypeButton = new javax.swing.JButton();
        mixinTypeButton = new javax.swing.JButton();
        mixinTypeLabel = new javax.swing.JLabel();
        mixinLabel = new javax.swing.JLabel();
        mixinCheckbox = new javax.swing.JCheckBox();
        jScrollPane1 = new javax.swing.JScrollPane();
        mixinList = new javax.swing.JList();
        mixinTypeButton1 = new javax.swing.JButton();

        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("offset/nodes/client/dialog/view/DialogBundle"); // NOI18N
        nameLabel.setText(bundle.getString("schema.nodePanel.name")); // NOI18N

        mandatoryLabel.setText(bundle.getString("schema.nodePanel.mandatory")); // NOI18N

        mandatoryCheckbox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        mandatoryCheckbox.setMargin(new java.awt.Insets(0, 0, 0, 0));

        multipleLabel.setText(bundle.getString("schema.nodePanel.multiple")); // NOI18N

        multipleCheckbox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        multipleCheckbox.setMargin(new java.awt.Insets(0, 0, 0, 0));

        nodeTypeLabel.setText(bundle.getString("schema.node.requiredType")); // NOI18N

        primaryTypeButton.setText(bundle.getString("schema.nodePanel.primaryType.select")); // NOI18N
        primaryTypeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selectType(evt);
            }
        });

        superTypeLabel.setText(bundle.getString("schema.nodeDefPanel.superType")); // NOI18N

        superTypeButton.setText(bundle.getString("schema.nodePanel.mixin.add")); // NOI18N
        superTypeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                selectSuperType(evt);
            }
        });

        mixinTypeButton.setText(bundle.getString("schema.nodePanel.mixin.add")); // NOI18N
        mixinTypeButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                addMixinType(evt);
            }
        });

        mixinTypeLabel.setText(bundle.getString("schema.nodeDefPanel.mixinType")); // NOI18N

        mixinLabel.setText(bundle.getString("schema.nodePanel.mixin")); // NOI18N

        mixinCheckbox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
        mixinCheckbox.setMargin(new java.awt.Insets(0, 0, 0, 0));

        mixinList.setModel(new DefaultListModel());
        jScrollPane1.setViewportView(mixinList);

        mixinTypeButton1.setText(bundle.getString("schema.nodePanel.mixin.remove")); // NOI18N
        mixinTypeButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                removeMixinType(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(multipleLabel)
                                .addGap(44, 44, 44)
                                .addComponent(multipleCheckbox))
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(mandatoryLabel)
                                    .addComponent(nameLabel))
                                .addGap(28, 28, 28)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 297, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(mandatoryCheckbox))))
                        .addContainerGap(427, Short.MAX_VALUE))
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(nodeTypeLabel)
                                    .addComponent(superTypeLabel))
                                .addGap(20, 20, 20)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                                    .addComponent(superTypeTextField)
                                    .addComponent(primaryTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 299, javax.swing.GroupLayout.PREFERRED_SIZE)))
                            .addComponent(mixinLabel)
                            .addGroup(layout.createSequentialGroup()
                                .addGap(84, 84, 84)
                                .addComponent(mixinCheckbox))
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(mixinTypeLabel)
                                .addGap(31, 31, 31)
                                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE)))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                            .addComponent(primaryTypeButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(superTypeButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(mixinTypeButton1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                            .addComponent(mixinTypeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                        .addGap(355, 355, 355))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 787, Short.MAX_VALUE)
                        .addGap(21, 21, 21))))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(20, 20, 20)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(nameLabel)
                    .addComponent(nameTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addGap(22, 22, 22)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(mandatoryLabel)
                    .addComponent(mandatoryCheckbox))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(multipleLabel)
                    .addComponent(multipleCheckbox))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 23, Short.MAX_VALUE)
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(nodeTypeLabel)
                    .addComponent(primaryTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(primaryTypeButton))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(superTypeLabel)
                    .addComponent(superTypeTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(superTypeButton))
                .addGap(30, 30, 30)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(mixinLabel)
                    .addComponent(mixinCheckbox))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(mixinTypeButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(mixinTypeButton1))
                    .addComponent(mixinTypeLabel)
                    .addComponent(jScrollPane1, 0, 0, Short.MAX_VALUE))
                .addGap(29, 29, 29))
        );
    }// </editor-fold>//GEN-END:initComponents
    private void selectType(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectType
        // setup chooser
        NodeTypeDefinition[] definitions = types.getArray();
        chooser = new TypeChooserDialog(null, true, definitions);

        chooser.setVisible(true);
        int rc = chooser.getReturnStatus();
        if (rc == TypeChooserDialog.RET_OK) {
            NodeTypeDefinition definition = chooser.getDefinition();
            primaryTypeTextField.setText(definition.getName().toString());
            superTypeTextField.setText(getSupertype(definition.getSupertypes()));
        }
    }//GEN-LAST:event_selectType

    private void selectSuperType(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectSuperType
          // setup chooser
        NodeTypeDefinition[] definitions = types.getArray();
        chooser = new TypeChooserDialog(null, true, definitions);
   
        chooser.setVisible(true);
        int rc = chooser.getReturnStatus();
        if (rc == TypeChooserDialog.RET_OK) {
            NodeTypeDefinition definition = chooser.getDefinition();
            superTypeTextField.setText(definition.getName().toString());
        }

}//GEN-LAST:event_selectSuperType

    private void addMixinType(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addMixinType
          // setup chooser
        NodeTypeDefinition[] definitions = types.getMixinTypes();
        chooser = new TypeChooserDialog(null, true, definitions);
   
        chooser.setVisible(true);
        int rc = chooser.getReturnStatus();
        if (rc == TypeChooserDialog.RET_OK) {
            NodeTypeDefinition definition = chooser.getDefinition();
            ((DefaultListModel)mixinList.getModel()).addElement(definition.getName().toString());
        }

}//GEN-LAST:event_addMixinType

    private void removeMixinType(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeMixinType
        int selected = mixinList.getSelectedIndex();
        if (selected < 0)
            return;
        
        ((DefaultListModel)mixinList.getModel()).remove(selected);
}//GEN-LAST:event_removeMixinType

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JCheckBox mandatoryCheckbox;
    private javax.swing.JLabel mandatoryLabel;
    private javax.swing.JCheckBox mixinCheckbox;
    private javax.swing.JLabel mixinLabel;
    private javax.swing.JList mixinList;
    private javax.swing.JButton mixinTypeButton;
    private javax.swing.JButton mixinTypeButton1;
    private javax.swing.JLabel mixinTypeLabel;
    private javax.swing.JCheckBox multipleCheckbox;
    private javax.swing.JLabel multipleLabel;
    private javax.swing.JLabel nameLabel;
    private javax.swing.JTextField nameTextField;
    private javax.swing.JLabel nodeTypeLabel;
    private javax.swing.JButton primaryTypeButton;
    private javax.swing.JTextField primaryTypeTextField;
    private javax.swing.JButton superTypeButton;
    private javax.swing.JLabel superTypeLabel;
    private javax.swing.JTextField superTypeTextField;
    // End of variables declaration//GEN-END:variables
}
